// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/test/embedded_test_server/http_connection.h"

#include <utility>

#include "net/base/net_errors.h"
#include "net/socket/stream_socket.h"

namespace net {
namespace test_server {

    HttpConnection::HttpConnection(std::unique_ptr<StreamSocket> socket,
        const HandleRequestCallback& callback)
        : socket_(std::move(socket))
        , callback_(callback)
        , read_buf_(new IOBufferWithSize(4096))
        , weak_factory_(this)
    {
    }

    HttpConnection::~HttpConnection()
    {
        weak_factory_.InvalidateWeakPtrs();
    }

    void HttpConnection::SendResponseBytes(const std::string& response_string,
        const SendCompleteCallback& callback)
    {
        if (response_string.length() > 0) {
            scoped_refptr<DrainableIOBuffer> write_buf(new DrainableIOBuffer(
                new StringIOBuffer(response_string), response_string.length()));

            SendInternal(callback, write_buf);
        } else {
            callback.Run();
        }
    }

    int HttpConnection::ReadData(const CompletionCallback& callback)
    {
        return socket_->Read(read_buf_.get(), read_buf_->size(), callback);
    }

    bool HttpConnection::ConsumeData(int size)
    {
        request_parser_.ProcessChunk(base::StringPiece(read_buf_->data(), size));
        if (request_parser_.ParseRequest() == HttpRequestParser::ACCEPTED) {
            callback_.Run(this, request_parser_.GetRequest());
            return true;
        }
        return false;
    }

    void HttpConnection::SendInternal(const base::Closure& callback,
        scoped_refptr<DrainableIOBuffer> buf)
    {
        while (buf->BytesRemaining() > 0) {
            int rv = socket_->Write(buf.get(), buf->BytesRemaining(),
                base::Bind(&HttpConnection::OnSendInternalDone,
                    base::Unretained(this), callback, buf));
            if (rv == ERR_IO_PENDING)
                return;

            if (rv < 0)
                break;
            buf->DidConsume(rv);
        }

        // The HttpConnection will be deleted by the callback since we only need to
        // serve a single request.
        callback.Run();
    }

    void HttpConnection::OnSendInternalDone(const base::Closure& callback,
        scoped_refptr<DrainableIOBuffer> buf,
        int rv)
    {
        if (rv < 0) {
            callback.Run();
            return;
        }
        buf->DidConsume(rv);
        SendInternal(callback, buf);
    }

    base::WeakPtr<HttpConnection> HttpConnection::GetWeakPtr()
    {
        return weak_factory_.GetWeakPtr();
    }

} // namespace test_server
} // namespace net
